/*
 * ==========================================================================*\
 * | $Id: ServerApplicationSupportStrategy.java 357 2011-02-17 19:21:31Z
 * vtwoods@gmail.com $
 * |*-------------------------------------------------------------------------*|
 * | Copyright (C) 2009 Virginia Tech | | This file is part of CloudSpace. | |
 * CloudSpace is free software; you can redistribute it and/or modify | it under
 * the terms of the GNU General Public License as published | by the Free
 * Software Foundation; either version 3 of the License, or | (at your option)
 * any later version. | | CloudSpace is distributed in the hope that it will be
 * useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General
 * Public License for more details. | | You should have received a copy of the
 * GNU General Public License | along with CloudSpace; if not, see
 * <http://www.gnu.org/licenses/>.
 * \*==========================================================================
 */

package student.web.internal;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.Sessions;

import student.web.SharedPersistentMap;
import student.web.internal.PersistentStorageManager.StoredObject;
import student.web.internal.converters.Alias;
import cloudspace.vm.VM;

import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.core.JVM;


// -------------------------------------------------------------------------
/**
 * Defines the support methods needed to implement server-specific storage
 * features for an {@link student.web.Application} that is running as a plain
 * old Java program, rather than in the context of a web server application.
 * 
 * @author Stephen Edwards
 * @author Last changed by $Author: vtwoods@gmail.com $
 * @version $Revision: 420 $, $Date: 2011-02-17 14:21:31 -0500 (Thu, 17 Feb
 *          2011) $
 */
public class ServerApplicationSupportStrategy extends LocalApplicationSupportStrategy
                implements
                ApplicationSupportStrategy 
{
    private static final Set<String> usedIds = new HashSet<String>();
    private static final String CACHE_LIST = ".cache_list";
    static
    {
        usedIds.add( CACHE_LIST );
    }

    // ----------------------------------------------------------
    /**
     * Get a parameter passed to this page in the query part of the URL.
     * 
     * @param name
     *            The name of the parameter to retrieve
     * @return The parameter's value on the current page, or null if there is
     *         none.
     */
    public String getPageParameter( String name )
    {
        return Executions.getCurrent().getParameter( name );
    }


    // ----------------------------------------------------------
    /**
     * Get a parameter stored in the current session.
     * 
     * @param name
     *            The name of the parameter to retrieve
     * @return The parameter's value in the current session, or null if there is
     *         none.
     */
    public Object getSessionParameter( String name )
    {
        
        Map<String, Object> sessionMap = getInternalSessionMap();
        return sessionMap.get( name );
    }
    private Map<String, Alias> getInternalAliasSessionMap()
    {
        VM current = VM.currentVM();
        String sessionMapId = ".sessionAliasMap-" + current.getLocalRoot();
        Session curSession = Sessions.getCurrent();
        @SuppressWarnings("unchecked")
        Map<String, Alias> sessionMap = (Map<String, Alias>)Sessions.getCurrent()
            .getAttribute( sessionMapId );
        if ( sessionMap == null )
        {
            sessionMap = new HashMap<String, Alias>();
            curSession.setAttribute( sessionMapId, sessionMap );
        }
        return sessionMap;
    }
    private Map<String, Object> getInternalSessionMap()
    {
        VM current = VM.currentVM();
        String sessionMapId = ".sessionMap-" + current.getLocalRoot();
        Session curSession = Sessions.getCurrent();
        @SuppressWarnings("unchecked")
        Map<String, Object> sessionMap = (Map<String, Object>)Sessions.getCurrent()
            .getAttribute( sessionMapId );
        if ( sessionMap == null )
        {
            sessionMap = new HashMap<String, Object>();
            curSession.setAttribute( sessionMapId, sessionMap );
        }
        return sessionMap;
    }
    


    // ----------------------------------------------------------
    /**
     * Remove a parameter stored in the current session, if it exists.
     * 
     * @param name
     *            The name of the parameter to remove
     * @return The removed value, if the parameter existed, or null if there is
     *         no value to remove.
     */
    public Object removeSessionParameter( String name )
    {
        return setSessionParameter( name, null );
    }


    // ----------------------------------------------------------
    /**
     * Store a value in the current session. If a value already exists for the
     * given name, it is replaced.
     * 
     * @param name
     *            The name of the parameter to store
     * @param value
     *            The value to store
     * @return The previous value in the current session associated with the
     *         given name, if there is one, or null otherwise.
     */
    public Object setSessionParameter( String name, Object value )
    {
        if(usedIds.contains( name ))
            throw new RuntimeException("This id is used by CloudSpace.  Please choose another Id");
        Map<String, Object> sessionMap = getInternalSessionMap();
        Object old = getSessionParameter( name );
        sessionMap.put( name, value );
        return old;
    }
    


    // ----------------------------------------------------------
    /**
     * Cause the web application to show a different web page in the user's web
     * browser.
     * 
     * @param url
     *            The new web page to show in the user's browser
     */
    public void showWebPage( String url )
    {
        Executions.sendRedirect( url );
    }


    // ----------------------------------------------------------
    /**
     * Retrieve the name of the current ZHTML file, such as "index.zhtml" or
     * "lab02.zhtml".
     * 
     * @return The name of the current ZHTML file, without any directory
     *         component, or "" if there is none.
     */
    public String getCurrentPageName()
    {
        String result = getCurrentPagePath();
        int pos = result.lastIndexOf( '/' );
        if ( pos >= 0 )
        {
            result = result.substring( 0, pos );
        }
        return result;
    }


    // ----------------------------------------------------------
    /**
     * Retrieve the relative path name of the current ZHTML file, such as
     * "/Fall09/mypid/index.zhtml" or "/Fall09/mypid/lab02/lab02.zhtml".
     * 
     * @return The name path to the current ZHTML file, or "" if there is none.
     */
    public String getCurrentPagePath()
    {
        return Executions.getCurrent().getDesktop().getRequestPath();
    }


    public Map<String, StoredObject> getPersistentCache( String cacheId )
    {
        Map<String, Map<String, StoredObject>> caches = getAllCaches();
        if(caches == null)
            return null;
        return caches.get( cacheId );
    }

    private Map<String, Map<String, StoredObject>> getAllCaches()
    {
        Object cacheMaps = Executions.getCurrent().getDesktop().getAttribute( CACHE_LIST );
        if(cacheMaps == null)
            return null;
        @SuppressWarnings("unchecked")
        Map<String, Map<String,StoredObject>> caches = (Map<String, Map<String,StoredObject>>) cacheMaps;
        return caches;
    }


    public Map<String, StoredObject> initPersistentCache(
        String cacheId)
    {
        Map<String, StoredObject> cache = new HashMap<String, StoredObject>();
        Map<String, Map<String, StoredObject>> caches = getAllCaches();
        if(caches == null)
        {
            caches = new HashMap<String,Map<String,StoredObject>>();
            Executions.getCurrent().getDesktop().setAttribute( CACHE_LIST, caches);
        }
        caches.put( cacheId, cache );
        return cache;
    }


    public Object getAlias( Object value )
    {
        String aliasId = getAliasId(value);
        if(aliasId == null)
            return value;
        else
            return new Alias(aliasId);
    }
    public Object resolveAlias(Object value)
    {
        if(value != null && value instanceof Alias)
        {
            //Ew.... my trick didnt work, if I dont grab a custom loader, i cant find the classes when i reload it.
            SharedPersistentMap<Object> pMap = new SharedPersistentMap<Object>(Object.class, VM.currentVM().getLoader());
            return pMap.get( ((Alias)value).getKey() );
        }
        return value;
    }
    private String getAliasId(Object value)
    {
        Map<String, Alias> aliasMap = getInternalAliasSessionMap();
        Map<String,Map<String,StoredObject>> allCaches = this.getAllCaches();
        if(allCaches == null)
            return null;
        for(String cacheId : allCaches.keySet())
        {
            Map<String,StoredObject> cache  = allCaches.get( cacheId );
            for(String cachedObject : cache.keySet())
                if(cache.get( cachedObject ) != null && cache.get( cachedObject ).value() == value)
                {
                    return cachedObject;
                }
            
        }
        return null;
    }
    public ReflectionProvider getReflectionProvider()
    {
        //Let xstream decide the best one.
        return (new JVM()).bestReflectionProvider();
    }
}
